/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * $Id: CoordinateDriveData.hpp,v 1.12 2009-09-25 09:11:24 morsko Exp $
 */
/*!
 * \file
 * \brief MaCI - CoordinateDrive interface data encoder / decoder
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 */
#ifndef _MACI_COORDINATEDRIVEDATA_HPP_
#define _MACI_COORDINATEDRIVEDATA_HPP_
#include "MaCIData.hpp"
#include "CoordinateDriveTypes.hpp"
#include <stdio.h>
#include "owndebug.h"

namespace gim {
  namespace binbag{class CBinBag;}
}

namespace MaCI {
  namespace CoordinateDrive {
    
    /** Data Encoder/Decoder for the CoordinateDrive Interface.
     *
     * Contains the real protocol encoder/decoder for the MaCI-CoordinateDrive
     * interface
     */
    class CCoordinateDriveData : public MaCI::CMaCIData
    {
    public:
      /** Constructor for CoordinateDriveData
       *
       */
      CCoordinateDriveData(void);

      /** Destructor for CCoordinateDriveData
       *
       */
      ~CCoordinateDriveData();
      

      /** Copy constructor for CoordinateDriveData. 
       *
       * The copy constructor duplicates the input object to maximum
       * depth (All containers are duplicated too). After a copy is
       * constructed, no dependencies to original Data element exist.
       *
       * @param[in] aCoordinateDriveData Reference to CCoordinateDrive container to copy.
       */
      CCoordinateDriveData(const CCoordinateDriveData &aCoordinateDriveData);

      /** Assignment operator for CoordinateDriveData.
       *
       * The assignment operator duplicates the input object to
       * maximum depth (All containers are duplicated too). After a
       * copy is constructed, no dependencies to original Data element
       * exist.
       *
       * @param[in] aCoordinateDriveData Reference to CCoordinateDriveData container to copy.
       */
      CCoordinateDriveData &operator=(const CCoordinateDriveData &aCoordinateDriveData);

      /** Decode a BinBag container as PositionData element.
       *
       */
      bool DecodeFrom(gim::binbag::CBinBag *aBinBag = NULL);
      
      /** Reset decoder/Encoder.
       *
       * Resets all fields, except leaves the current BinBag container
       * pointer intact.
       */
      void Reset();
      
      /** Set Timestamp element.
       * 
       * \see Common::TTimestamp
       *
       * @param[in] aStamp      Timestamp element to set.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool SetTimestamp(const Common::TTimestamp &aStamp);
      
      /**
       * Sets a TCoordinate2D structure to CoordinatDriveData. If you wan't to 
       * add multiple coordinates(>1), you have to add first TCoordinateHeader 
       * with SetCoordinateHeader-functions. 
       *
       * @param aCoordinate    Structure to add
       * @return               True if adding succeeded
       */
      bool SetCoordinate2D(const TCoordinate2D &aCoordinate);

      /**
       * Sets a TTargetLimitDistance structure to CoordinateDriveData
       *
       * @param aLimit         Structure to add
       * @return               True if adding succeeded
       */
      bool SetTargetLimitDistance(const TTargetLimitDistance &aLimit);

      /**
       * Sets a TDriveMode structure to CoordinateDriveData. 
       * If you wan't to add multiple driveModes(>1), you have to add 
       * first TDriveModeHeader with SetDriveModeHeader-functions.
       *
       * @param aMode          Structure to add
       * @return               True if adding succeeded
       */
      bool SetDriveMode(const TDriveMode &aMode);

      /**
       * Sets a TCommand structure to CoordinateDriveData. 
       * NOTE: You have to set command as a _FIRST_ element. otherwise
       * it will not work. (will fix it someday)
       *
       * @param aCommand       Structure to add
       * @return               True if adding succeeded
       */
      bool SetCommand(const TCommand &aCommand);

      /**
       * Sets a TCoordinateHeader structure to CoordinateDriveData. It is used
       * when sending multiple coordinates at the same time. Header is just to 
       * inform how many TCoordinate2D are coming after this header
       *
       * @param[in] aHeader    Structure to add
       * @return               True if adding succeeded
       */
      bool SetCoordinateHeader(const TCoordinateHeader &aHeader);

      /**
       * Sets a TDriveModeHeader structure to CoordinateDriveData. It is used
       * when sending multiple drive modes at the same time. Header is just to 
       * inform how many TDriveMode are coming after this header.
       *
       * @param[in] aHeader    Structure to add
       * @return               True if adding succeeded
       */
      bool SetDriveModeHeader(const TDriveModeHeader &aHeader);

      /**
       * Sets a TSpeed structure to CoordinateDriveData. It is used
       * when setting new driving speed and for gettinf maximum driving speed from server.
       *
       * @param[in] aSpeed     Structure to add
       * @return               True if adding succeeded
       */
      bool SetSpeed(const TSpeed &aSpeed);

      /** Get pointer to Common::TTimestamp element.
       *
       * \see Common::TTimestamp
       * 
       * @return                Constant pointer to timestamp
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const Common::TTimestamp *GetTimestamp(void) const{
        return iTimestampPtr;
      }

      /**
       * Returns the TTargetLimitDistance structure pointer set to CoordinateDriveData
       *
       * @return               TTargetLimitDistance-structure pointer
       */
      const TTargetLimitDistance *GetTargetLimitDistance(void) const {
	return iTargetLimitDistancePtr;
      }


      /**
       * Returns the driveMode structure pointer set to CoordinateDriveData
       *
       * @return               TDriveMode-structure pointer
       */
      const TDriveMode * GetDriveMode(const int aIndex) const{
	const TDriveMode *elem = NULL;
	

	if (aIndex >= 0 && 
	    aIndex < iDriveModeElementCount && 
	    iDriveModePtr != NULL) {
	  
	  const char *cptr = (const char *)iDriveModePtr;
	  cptr += aIndex * (sizeof(TDriveMode) + sizeof(gim::binbag::TBinBlob));
	  
	  // Now, typecast the address to Element.
	  elem = (const TDriveMode *)cptr;
	}
	
	
	return elem;

      }

      /**
       * Returns the command structure pointer set to CoordinateDriveData
       *
       * @return               TCommand-structure pointer
       */
      const TCommand *GetCommand(void) const {
        return iCommandPtr;
      }
      
      /**
       * Returns the CoordinateHeader structure pointer set to CoordinateDriveData
       *
       * @return               TCoordinateHeader-structure pointer
       */
      const TCoordinateHeader *GetCoordinateHeader(void) const{

	return iCoordinateHeaderPtr;
      }

      /**
       * Returns the Coordinate structure pointer with wanted index
       * set to CoordinateDriveData. CoordinateHeader tells how many Coordinates
       * can be found at CoordinateDriveData
       *
       * @return               TCoordinate-structure pointer
       */
      const TCoordinate2D *GetCoordinate(const int aIndex) const{
	const TCoordinate2D *elem = NULL;
	

	if (aIndex >= 0 && 
	    aIndex < iCoordinateElementCount && 
	    iFirstCoordinatePtr != NULL) {
	  
	  const char *cptr = (const char *)iFirstCoordinatePtr;
	  cptr += aIndex * (sizeof(TCoordinate2D) + sizeof(gim::binbag::TBinBlob));
	  
	  // Now, typecast the address to Element.
	  elem = (const TCoordinate2D *)cptr;
	}
	
	
	return elem;

      }

      /**
       * Returns the DriveModeHeader structure pointer set to CoordinateDriveData
       *
       * @return               TDriveModeHeader-structure pointer
       */
      const TDriveModeHeader *GetDriveModeHeader(void) const{
	return iDriveModeHeaderPtr;
      }

      /**
       * Returns the DriveModeHeader structure pointer set to CoordinateDriveData
       *
       * @return               TDriveModeHeader-structure pointer
       */
      const TSpeed *GetSpeed(void) const{
	return iSpeedPtr;
      }
      /**
       * Prints the contain of the coordinateDriveData
       *
       * @param level The debug print level
       */
      void Print(const int level) const;

      /** 
       * Tells if the currrent CoordinateDriveData is valid. It's not valid if it's empty,
       * or there are less coordinates/driveModes than said in the header
       *
       * @return        Boolean value if the data is valid
       */
      bool IsValid(void);

    protected:
    private:

      bool iValid;
      const Common::TTimestamp *iTimestampPtr; ///< Pointer to timestamp if exists
      const TCoordinate2D *iFirstCoordinatePtr;
      const TTargetLimitDistance *iTargetLimitDistancePtr;
      const TDriveMode *iDriveModePtr;
      const TCommand *iCommandPtr;
      const TCoordinateHeader *iCoordinateHeaderPtr;
    
      const TDriveModeHeader *iDriveModeHeaderPtr;
      const TSpeed *iSpeedPtr;
      int iCoordinateElementCount;
      int iHeaderCoordinateElementCount;
      int iDriveModeElementCount;
      int iHeaderDriveModeElementCount;
    };
  }
}
#endif  ///COORDINATEDRIVEDATA
